在博客 Android 插件化 – 类的动态加载实践 中我们通过一个实例来实践了类的动态加载以及使用。接下来从源码角度来分析一下类的动态加载是如何实现的。
流程图
1 | ├── BaseDexClassLoader |
构造方法
先来看一下 BaseDexClassLoader
的构造方法:
1 | public BaseDexClassLoader(String dexPath, File optimizedDirectory, |
这里除了调用父类 ClassLoader
的构造方法之外,就是创建了 DexPathList
对象。
1 | private ClassLoader(Void unused, ClassLoader parent) { |
ClassLoader
的构造方法很简单,就是把参数中的父 ClassLoader
赋值给成员变量。
DexPathList
下面来看一下 DexPathList
构造方法:
1 | public DexPathList(ClassLoader definingContext, String dexPath, |
下面来看一下 DexPathList.makeElements
代码:
1 | private static Element[] makeElements(List<File> files, File optimizedDirectory, |
ClassLoader
构造方法部分我们分析完了,其实整个过程就是调用了 DexPathList
的构造函数,把 dex 文件或者 Native libraries 加载到 Element
数组的过程。
体现在 DexPathList
中的 dexElements
和 nativeLibraryPathElements
成员变量,供 DexPathList.findClass
和 DexPathList.findLibrary
方法查询。
上面的方法中调用了 DexPathList.loadDexFile
方法:
1 | private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, |
这里可以看到,针对是否指定 optimizedDirectory
做了不同的处理。这里其实是分别调用了不同的 DexFile
构造方法。
DexFile
1 | private static Object openDexFile(String sourceName, String outputName, int flags, |
DexFile
主要就是调用了 native 方法 openDexFileNative
来打开 dex 文件。
加载类
下面来看一下类的加载过程,这里我们要关注一下 ClassLoader 的双亲委托机制:
1 | protected Class<?> loadClass(String name, boolean resolve) |
findClass
方法其实调用的是 BaseDexClassLoader
的 findClass
方法:
1 | @Override |
其实调用的是 DexPathList.findClass
方法:
1 | public Class findClass(String name, List<Throwable> suppressed) { |
类的加载重点在 DexFile.defineClass
方法,这里面会调用 native 方法 defineClassNative
。这里面的代码暂时不做分析。
总结
从上面分析我们可以看出,在构造 DexClassLoader
和 PathClassLoader
时,会加载 jar/apk/zip/dex 中的 dex 文件以及 Native libraries 并保存到 Element
数组中。后面动态加载类时都是从保存到数组中的 dex 文件中搜索,并调用 DexFile
的 Native 方法进行加载的过程。Native 方法这里暂不分析,有兴趣的大家可以自行研究。